#include "streamQueue.h"
#include <unistd.h>

void sq_init(SQ* sq) {
    sq->front = sq->rear = sq->nextFront = 0;
}

void sq_updateRear(SQ* sq, size_t size) {
    sq->rear = sq->rear + size;
}

/*
 * 데이터가 있는지 확인한다.
 * 리턴값이 0이면 데이터가 없다.
 * 리턴값이 1이면 sq->buffer[sq->front]가 valid한 데이터의 앞을 가리키고 있다.
 */
int sq_updateFront(SQ* sq) {
    HEADER* header;
    unsigned int next = 0;

    if (sq->front == sq->rear) {
        return 0;
    }

    if (sq->bufferSize - sq->min_available_size < sq->front ) {
        sq->front = 0;
        return 0;
    }

    header = (HEADER*)(&(sq->buffer[sq->front]));
    next = (sq->front + sizeof(HEADER) + header->size);

    if (sq->front <= sq->rear && sq->rear < next) {
        return 0;
    }

    sq->nextFront = next;
    return 1;
}

void sq_updateFrontDone(SQ* sq)
{
    sq->front = sq->nextFront;
}

int sq_full(SQ* sq) {
    if (sq->bufferSize - sq->min_available_size < sq->rear) {
        if (0 == sq->front|| sq->rear < sq->front) {
            return 1;
        }
        sq->rear = 0;
    }

    if (sq->rear < sq->front && sq->front <= (sq->rear + sizeof(HEADER) + sq->max_unit_size) ) {
        return 1;
    }
    return 0;
}

size_t sq_emptySize(SQ* sq)
{
    unsigned int r = sq->rear , f = sq->front;
    if (f <= r) {
        return (sq->bufferSize - r + f);
    }
    else {
        return (f - r);
    }
}

unsigned char* sq_rear(SQ* sq) {
    return &(sq->buffer[sq->rear]);
}

unsigned char* sq_front(SQ* sq) {
    return &(sq->buffer[sq->front]);
}


////////////////////////////////////////////////////////////////////////////////////
// check 및 사용 예제 코드
////////////////////////////////////////////////////////////////////////////////////

#include <sys/time.h>

#define BUFSIZE 100000
#define MAX_UNIT_SIZE 1000
#define MAGIC 0x345F23D 
int main(int argc,char** argv)
{
    unsigned char buffer[BUFSIZE];
    SQ sq;

    sq_init(&sq);
    sq.buffer = buffer;
    sq.bufferSize = BUFSIZE;
    sq.min_available_size = 5000;
    sq.max_unit_size = MAX_UNIT_SIZE;

    struct timeval tv;
    int i;
    HEADER* header;
    unsigned long long id = 1;
    unsigned long long prevId = 0;

    gettimeofday(&tv, NULL);
    srand(tv.tv_usec % 1000);
    while(1) {
        srand(rand());

        if (id > 100000000) {
            printf("test passed!\n");
            return 0;
        }

        /*
         * 아래 코드는 streamQueue에 데이터를 넣는 방법 예제가 된다.
         */
        for (i = 0; i < 100; i++) {

            if (!sq_full(&sq)) {

                // fill header
                header = sq_rear(&sq);
                header->magic = MAGIC;
                header->size = (rand() % MAX_UNIT_SIZE);
                if (header->size < 100) header->size = 100; 
                header->id = (id++);

                sq_updateRear(&sq, sizeof(HEADER) + header->size); 
            }
            else {
                break;
            }
        }

        /*
         * 아래 코드는 streamQueue에 데이터가 있는지를 확인하고 꺼내는 부분의 예가 된다.
         */

        while ( sq_updateFront(&sq) ) {

            // 데이터가 있으면 front가 가리키는 부분의 주소를 얻는다.
            header = sq_front(&sq);

            // 검증부분
            if (header->magic != MAGIC) {
                printf("Error %d\n", __LINE__);
                return -1;
            }

            if (prevId + 1 != header->id) {
                printf("Error %d\n", __LINE__);
                return -1;
            }
            prevId = header->id;

            // 데이터 사용이 끝나면 아래를 불러서 front를 이동
            sq_updateFrontDone(&sq);
        }
    }

    return 0;
}







